package com.mdp.lcode.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mdp.core.entity.LangTips;
import com.mdp.core.utils.BaseUtils;
import com.mdp.core.utils.DateUtils;
import com.mdp.core.utils.ObjectTools;
import com.mdp.lcode.FormUtil;
import com.mdp.lcode.entity.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.mdp.core.service.BaseService;
import com.mdp.core.entity.Tips;
import com.mdp.core.err.BizException;

import com.mdp.lcode.mapper.FormDataMapper;
import org.springframework.util.StringUtils;

/**
 * 父类已经支持增删改查操作,因此,即使本类什么也不写,也已经可以满足一般的增删改查操作了.<br> 
 * 组织 com  顶级模块 mdp 大模块 form 小模块 <br>
 * 实体 FormData 表 form_data 当前主键(包括多主键): id; 
 ***/
@Service
public class FormDataService extends BaseService<FormDataMapper,FormData> {
	static Logger logger =LoggerFactory.getLogger(FormDataService.class);

	/**
	 * 自定义查询，支持多表关联
	 * @param page 分页条件
	 * @param ew 一定要，并且必须加@Param("ew")注解
	 * @param ext 如果xml中需要根据某些值进行特殊处理，可以通过这个进行传递，非必须，注解也可以不加
	 * @return
	 */
	public List<Map<String,Object>> selectListMapByWhere(IPage page, QueryWrapper ew, Map<String,Object> ext){
		return baseMapper.selectListMapByWhere(page,ew,ext);
	}


	@Autowired
	FormFieldService  formFieldService;

	@Autowired
	FormDataFlowService formDataProcessApprovaService;

	/**
	 * 更新tagIds,tagNames两个字段为最新的值
	 * @param dataId
	 * @param tagIds
	 * @param tagNames
	 */
	public void updateTagsByDataId(String dataId, String tagIds, String tagNames) {
		UpdateWrapper<FormData> uw=new UpdateWrapper<>();
		uw.set("tag_ids",tagIds);
		uw.set("tag_names",tagNames);
		uw.eq("id",dataId);
		super.update(uw);

	}



	/**
	 * 创建业务主键
	 * @param formDefVo
	 * @param formData
	 * @return
	 */
	public String createBizKey(FormDefVo formDefVo,FormData formData){
		FormDef formDef=formDefVo.getFormDef();
		if(ObjectTools.isEmpty(formDef.getPks())){
			return "";
		}

		String[] pkIds=formDef.getPks().split(",");
		List<String> extInfosPkIds= Arrays.stream(pkIds).filter(k->k.indexOf(".")>=0).collect(Collectors.toList());
		JSONArray extInfos=new JSONArray();
		if(extInfosPkIds.size()>0){
			extInfos= JSON.parseArray(formData.getExtInfos());
		}
		List<String> bizKeys=new ArrayList<>();
		Map<String,Object> formDataMap=null;
		for (String pkId : pkIds) {
			if(pkId.indexOf(".")<0){
				if(pkId.startsWith("ctime:")){
					String formate=transNum2String(pkId.split(":")[1]);
					String ctime=DateUtils.format(formData.getCtime(),formate);
					bizKeys.add(ctime);
				}else if("cuserid".equals(pkId)){
					bizKeys.add(formData.getCuserid());
				}else if("deptid".equals(pkId)){
					bizKeys.add(formData.getDeptid());
				}else{
					if(formDataMap==null){
						formDataMap=BaseUtils.toMap(formData);
					}
					String val= (String) formDataMap.get(pkId);
					if(ObjectTools.isEmpty(val)){
						throw new BizException(LangTips.errMsg("pk-field-required","%s不能为空",pkId));

					}
				}
			}else{
				String pkIdTrim=pkId.replaceAll("\\w*[.]","");

				Optional<Object> options=extInfos.stream().filter(e->((JSONObject)e).getString("id").equals(pkIdTrim)).findAny();
				if(!options.isPresent()){
					throw new BizException(LangTips.errMsg("pk-field-required","%s不能为空",pkId));
				}
				bizKeys.add(((JSONObject)(options.get())).getString("value"));
			}
		}
		return bizKeys.stream().collect(Collectors.joining(","));
	}

	public String transNum2String(String num){
		Map<String,String> m=new HashMap<>();
		m.put("1","yyyy");
		m.put("2","yyyy-MM");
		m.put("3","yyyy-MM-dd");
		m.put("4","yyyy-MM-dd HH");
		m.put("5","yyyy-MM-dd HH:mm");
		m.put("6","yyyy-MM-dd HH:mm:ss");
		 return m.get(num);
	}

	/**
	 * 流程结束时调用更新数据状态未2 审核结束
	 * @param flowState
	 * @param formId
	 * @param procInstId
	 */
	public void updateFstateByProcInst(String flowState,String formId,String procInstId) {
		Map<String,Object> p=new HashMap<>();
		p.put("flowState", flowState);
		p.put("formId", formId);
		p.put("procInstId", procInstId);

		this.baseMapper.updateFlowStateByProcInst(p);
	}
	/**
	 * 低代码直接操作时调用，检查流程状态，如果是审核通过和审核中，不允许更改数据
	 * @param formData
	 * @return
	 */
	public  int insertOrUpdate(FormDataVo formData, boolean checkFstate) {
		String formId=formData.getFormId();
		FormDefVo formDefVo=formFieldService.getFormFieldFromCache(formId);
		String bizKey=this.createBizKey(formDefVo, formData);
		formData.setBizKey(bizKey);
		if(StringUtils.isEmpty(formData.getId())){
			formData.setId(this.createKey("id"));
			formData.setCtime(new Date());
			formData.setLtime(new Date());
			FormData fd=this.getDbFormDataByBizKey(formId,formData.getBizKey());
			if(fd!=null) {
				throw new BizException("exists-form-data-biz-key","bizKey","数据已经存在不能再添加");
			}else {
				return this.insert(formData);
			}
		}else{
			formData.setLtime(new Date());
			FormData fd=this.getDbFormDataByBizKey(formId,formData.getBizKey());

			if(fd==null) {//如果通过bizKey查不出来
				FormData fdObject=getById(formData.getId());
				if(fdObject!=null) {//通过id可以查出来，通过bizKey查不出来，说明是修改了bizKey,可以更新
					if(checkFstate) {
						if("0".equals(fdObject.getFstate())) {
							formData.setLtime(new Date());
							return super.updateByPk(formData);
						}else {
							if("1".equals(fdObject.getFstate())) {
								throw new BizException("flow-state-error-001","审核中的数据不允许修改");
							}else if("2".equals(fdObject.getFstate())) {
								throw new BizException("flow-state-error-002","审核完毕的数据不允许修改");
							}else {
								throw new BizException("flow-state-error-003","数据状态不正确");
							}

						}
					}else {
						formData.setLtime(new Date());
						return super.updateByPk(formData);
					}
				}else {//通过id也查不出来，通过bizKey查不出来，说明是新增
					formData.setCtime(new Date());
					formData.setLtime(new Date());
					if(StringUtils.isEmpty(formData.getFstate())) {
						formData.setFstate("0");
					}
					return this.insert(formData);
				}

			}else {//如果通过bizKey查得出来

				//如果id相同，说明是同一条数据
				if(fd.getId().equals(formData.getId())) {
					if(checkFstate) {
						if("0".equals(fd.getFstate())) {
							formData.setLtime(new Date());
							return super.updateByPk(formData);
						}else {
							if("1".equals(fd.getFstate())) {
								throw new BizException("flow-state-error-001","审核中的数据不允许修改");
							}else if("2".equals(fd.getFstate())) {
								throw new BizException("flow-state-error-002","审核完毕的数据不允许修改");
							}else {
								throw new BizException("flow-state-error-003","数据状态不正确");
							}
						}
					}else {
						formData.setLtime(new Date());
						return super.updateByPk(formData);
					}
				}else {//如果id不相同，说明不是同一条数据
					throw new BizException("form-data-biz-key-exists","数据已经存在，不能继续添加，请检查是否关键数据错误");
				}

			}
		}
	}

	/**
	 * 工作流发起和执行过程中调用，不更新flowState，只更改数据
	 * 不检查数据状态
	 * @param formDatas
	 */
	public void batchInsertOrUpdate(List<FormDataVo> formDatas,boolean updateFstate) {
		String formId=formDatas.get(0).getFormId();
		FormDefVo fromDefVo=this.formFieldService.getFormFieldFromCache(formId);

		if(fromDefVo==null) {
			throw new BizException("表单字段不存在，无法完成表单数据保存");
		}
		List<FormField> formFields=fromDefVo.getFormFields();
		Tips tips=new Tips("表单检验成功");
		List<FormDataVo> needInsertList=new ArrayList<>();
		List<FormDataVo> needUpdateList=new ArrayList<>();

		for (FormDataVo formData : formDatas) {
			String bizKey=createBizKey(fromDefVo,formData);
			formData.setBizKey(bizKey);
			FormData fd=this.getDbFormDataByBizKey(formId,formData.getBizKey());
			if(fd==null) {//如果通过bizKey查不出来
				FormData fdObject=this.getById(formData.getId());
				if(fdObject!=null) {//通过id可以查出来，通过bizKey查不出来，说明是修改了bizKey,可以更新
					formData.setLtime(new Date());
					needUpdateList.add(formData);
				}else {//通过id也查不出来，通过bizKey查不出来，说明是新增
					formData.setCtime(new Date());
					formData.setLtime(new Date());
					formData.setFstate("1");
					needInsertList.add(formData);
				}

			}else {//如果通过bizKey查得出来

				//如果id相同，说明是同一条数据
				if(fd.getId().equals(formData.getId())) {
					formData.setLtime(new Date());
					needUpdateList.add(formData);
				}else {//如果id不相同，说明不是同一条数据
					throw new BizException("form-data-biz-key-exists","数据已经存在，不能继续添加，请检查是否关键数据错误");
				}

			}

		}
		if(needInsertList.size()>0) {
			this.saveBatch(needInsertList.stream().map(k->(FormData)k).collect(Collectors.toList()));
		}

		if(needUpdateList.size()>0) {
			if(updateFstate) {
				this.updateBatchById(formDatas.stream().map(k->(FormData)k).collect(Collectors.toList()));
			}else {
				this.updateBatchById(formDatas.stream().map(k->{
					 k.setFstate(null);
					 return (FormData)k;
				}).collect(Collectors.toList()));

			}

		}


	}

	/**
	 * 流程审批过程中回调该接口，更新业务数据
	 * 如果发起流程时上送了restUrl，则无论流程中是否配置了监听器都会在流程发生以下事件时推送数据过来
	 * eventName: PROCESS_STARTED 流程启动完成 全局
	 *            PROCESS_COMPLETED 流程正常结束 全局
	 *            PROCESS_CANCELLED 流程删除 全局
	 *            create 人工任务启动
	 *            complete 人工任务完成
	 *            assignment 人工任务分配给了具体的人
	 *            delete 人工任务被删除
	 *            TASK_COMPLETED_FORM_DATA_UPDATE 人工任务提交完成后，低代码数据更新
	 *
	 * 其中 create/complete/assignment/delete事件是需要在模型中人工节点上配置了委托代理表达式 ${taskBizCallListener}才会推送过来。
	 * 在人工任务节点上配置 任务监听器  建议事件为 complete,其它assignment/create/complete/delete也可以，一般建议在complete,委托代理表达式 ${taskBizCallListener}
	 *
	 * @param flowVars {flowBranchId,agree,procInstId,startUserid,assignee,actId,taskName,mainTitle,branchId,bizKey,commentMsg,eventName,modelKey} 等
	 * @return 如果tips.isOk==false，将影响流程提交
	 **/
	public void processApprova(Map<String, Object> flowVars) {
		String procInstId=(String) flowVars.get("procInstId");
		String eventName=(String) flowVars.get("eventName");
		String formId=(String) flowVars.get("formId");
		String agree=(String) flowVars.get("agree");

		if(FormUtil.TASK_COMPLETED_FORM_DATA_UPDATE.equals(eventName) || FormUtil.TASK_COMPLETED.equals(eventName)) {
			Map<String,Object> formDatasMap=( Map<String,Object>) flowVars.get("formData");

			if(formDatasMap!=null && !formDatasMap.isEmpty()) {
				FormDataVo formDataOrigin=BaseUtils.fromMap(formDatasMap, FormDataVo.class);
				String fieldIds= (String) flowVars.get(FormUtil.NEED_UPDATE_FORM_DATA_FIELD_ID_LIST);
				FormDefVo formDefVo=this.formFieldService.getFormFieldFromCache(formId);
				boolean needUpdateBizKey=false;
				Map<String,Object> needUpdateFormDataMap=new HashMap<>();
				if(ObjectTools.isEmpty(fieldIds)||"all".equalsIgnoreCase(fieldIds)){
					needUpdateFormDataMap.putAll(formDatasMap);
				}else if(!"none".equalsIgnoreCase(fieldIds)) {

					for (String fieldId : fieldIds.split(",")) {
						Object value=formDatasMap.get(fieldId);
						needUpdateFormDataMap.put(fieldId, value);
						if(formDefVo.getFormFields().stream().filter(k->k.getIdCamel().equals(fieldId)).findAny().isPresent()){
							needUpdateBizKey=true;
						}
					}
				}
				if(!needUpdateFormDataMap.isEmpty()){
					String bizKey=this.createBizKey(formDefVo, formDataOrigin);
					if(needUpdateBizKey){
						needUpdateFormDataMap.put("bizKey", bizKey);
					}
					this.updateSomeFieldsByFormDataId(needUpdateFormDataMap);
				}

			}
		}else {

			if("PROCESS_STARTED".equals(eventName)) {
				Map<String,Object> formDatasMap=( Map<String,Object>) flowVars.get("formData");
				List<FormDataVo> formDatas=new ArrayList<>();
				List<FormDataFlow> approvas=new ArrayList<>();
				if(formDatasMap!=null && formDatasMap.size()>0) {
					FormDataVo formDataVo=BaseUtils.fromMap(formDatasMap, FormDataVo.class);
					FormDefVo formDefVo=this.formFieldService.getFormFieldFromCache(formId);
					String bizKey=this.createBizKey(formDefVo,formDataVo);
					formDataVo.setBizKey(bizKey);
					if(StringUtils.isEmpty(formDataVo.getId())) {
						formDataVo.setId(this.createKey("id"));
					}
					formDataVo.setFstate("1");
					formDatas.add(formDataVo);

					FormDataFlow approva=BaseUtils.fromMap(flowVars, FormDataFlow.class);
					approva.setBranchId(approva.getBranchId());
					approva.setFormDataId(formDataVo.getId());
					approva.setFormId(formDataVo.getFormId());
					approva.setFlowLastTime(new Date());
					approva.setFlowState("1");
					approva.setId(this.createKey("id"));
					approvas.add(approva);
					this.batchInsertOrUpdate(formDatas, true);
					this.formDataProcessApprovaService.batchInsert(approvas);
				}
			}else if("PROCESS_COMPLETED".equals(eventName)) {
				if("1".equals(agree)) {
					this.updateFstateByProcInst("2", formId, procInstId);
				}else {
					this.updateFstateByProcInst("3", formId, procInstId);
				}

			}else if("PROCESS_CANCELLED".equals(eventName)) {
				this.updateFstateByProcInst("4", formId, procInstId);
			}
		}
	}

	private int updateSomeFieldsByFormDataId(Map<String, Object> needUpdateFormDataMap) {
		FormData formData=BaseUtils.fromMap(needUpdateFormDataMap,FormData.class);
		return super.updateById(formData,true)?1:0;
	}



	public FormData getDbFormDataByBizKey(String formId,String bizKey){
		QueryWrapper<FormData> qw=new QueryWrapper<>();
		qw.eq("form_Id",formId);
		qw.eq("biz_key",bizKey);
		List<FormData> formDatas= super.list(qw);
		if(formDatas!=null && formDatas.size()>0){
			return formDatas.get(0);
		}else{
			return null;
		}
	}

    public long countByFormId(String formId) {
		QueryWrapper<FormData> qw=new QueryWrapper<>();
		qw.eq("form_id",formId);
		return super.count(qw);
    }
}

